package com.koron.auth.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.koron.auth.aop.LoginLog;
import com.koron.auth.service.AuthService;
import com.koron.auth.service.StaffService;
import com.koron.auth.util.RSATool;
import com.koron.bean.app.AppSimpleBean;
import com.koron.bean.base.Response;
import com.koron.bean.logBean.LoginLogBean;
import com.koron.bean.system.org.OrgVo;
import com.koron.bean.system.role.UserOwnJurisdictionBean;
import com.koron.bean.system.staff.Constant;
import com.koron.bean.system.staff.dto.OutStaffBean;
import com.koron.bean.system.staff.vo.EamUser;
import com.koron.common.core.business.system.SystemCacheUtil;
import com.koron.common.core.business.workflow.util.HttpUtil;
import com.koron.common.core.util.qywx.QywxMessageUtil;
import com.koron.common.core.util.qywx.QywxUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.http.HttpStatus;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.*;

/**
 * @ClassName AuthServiceImpl
 * @description
 * @Author zhouj
 * @Date 2021/7/14 20:09
 */
@Slf4j
@Service
@RefreshScope
public class AuthServiceImpl implements AuthService {

    @Autowired
    StaffService staffService;

    @Value("${sso.redirectPage}")
    private String redirectPage;

    @Value("${sso.redirectWorkStationPage}")
    private String workStationPage;

    @Value("${sso.redirectUrl}")
    private String redirectUrl;

    @Value("${sso.casTicketUrl:null}")
    private String casTicketUrl;

    @Value("${sso.casType:uma}")
    private String casType;

    @Value("${oa.redirectPage}")
    private String oaRedirectPage;

    @Value("${oa.modulus}")
    private String modulus;

    @Value("${oa.publicString}")
    private String publicString;

    @Value("${eam.LoginUrl}")
    private String eamLoginUrl;

    @Autowired
    QywxUtil qywxUtil;

    @Autowired
    QywxMessageUtil qywxMessageUtil;

    @Autowired
    IamAuthServiceImpl iamAuthService;
    @Autowired
    ZsjAuthServiceImpl zsjAuthService;
    @Autowired
    RemoteAuthServiceImpl remoteAuthService;

    @Override
    public Response<Object> login(String account) {

        account = StringUtils.isEmpty(account) ? "test" : account;

        //查询用户信息，生成token，并将用户权限等信息存在redis
        Response<Object> response = staffService.queryStaffInfo(account);

        if (response.getCode() != 200)
            return response;

        Map<String, Object> data = (Map<String, Object>) response.getData();

        EamUser user = JSON.parseObject(JSON.toJSONString(data), EamUser.class);

        if (user == null)
            return Response.ok();

        //cas认证通过，然后根据信息返回token
        String token = SystemCacheUtil.getNewToken(account, user.getName());

        //用户信息存入redis,返回token
        SystemCacheUtil.cacheUserInfo(user, token);

        return Response.ok(token);
    }

    @Override
    public void iamSsoLoginRedirect(String code, HttpServletRequest request, HttpServletResponse response) throws IOException {
        iamAuthService.ssoLoginRedirect(code, request, response);
    }

    @Override
    public void zsjSsoLoginRedirect(String ticket, HttpServletRequest request, HttpServletResponse response) throws IOException {
        zsjAuthService.ssoLoginRedirect(ticket,request,response);
    }

    //直接重定向到前台
    @Override
    public void ssoLoginRedirect(String ticket, HttpServletRequest request, HttpServletResponse response) throws IOException {
        //在此判断如果用户只有一个租户、一个组织、一个身份，登录之后直接跳转到首页
        if (!StringUtils.isEmpty(ticket)) {

            Response msg = getAccountByTicket(ticket);

            // 查询条件字符串转对象，查询数据结果
            // ticket有错，可返回异常信息，前端可重定向到系统登录界面，也可在此用response.sendRedirect直接重定向到登录界面
            if (msg.getCode() != 0) {
                response.setContentType("text/html; charset=UTF-8");
                response.getWriter().println("未找到集团单点登录信息:" + ticket);
                return;
            }
            String account = (String) msg.getData();
            if (StringUtils.isEmpty(account)) {
                response.setContentType("text/html; charset=UTF-8");
                response.getWriter().println("未找到集团单点登录信息:" + ticket);
                return;
            }

            // 校验本系统用户表是否有对应账号
            Response res = staffService.queryStaffInfo(account);
            if (!(res.getCode() == 200 && res.getData() != null)) {
                response.setContentType("text/html; charset=UTF-8");
                response.getWriter().println("系统没有该账号信息:" + account);
                return;
            }

            EamUser eamUser = JSON.parseObject(JSON.toJSONString(res.getData()), EamUser.class);

            if (Objects.isNull(eamUser)) {
                response.setContentType("text/html; charset=UTF-8");
                response.getWriter().println("系统没有该账号信息:" + account);
                return;
            }

            if (defaultInfo(eamUser, response)) {
                return;
            }
        }

        log.info("auth 重定向前台页面:{}", redirectPage);
        response.sendRedirect(redirectPage + ticket);
    }

    public Boolean defaultInfo(EamUser eamUser, HttpServletResponse response) throws IOException {

        List<OrgVo> orgVoList = eamUser.getOrgVoList();
        List<AppSimpleBean> appSimpleBeanList = eamUser.getAppSimpleBeanList();

        if (CollectionUtils.isNotEmpty(orgVoList) && orgVoList.size() == 1
                && CollectionUtils.isNotEmpty(appSimpleBeanList)
                && appSimpleBeanList.size() == 1) {

            //返回登录成功信息，前端可做跳转首页处理或者后台直接重定向到系统首页
            //cas认证通过，然后根据信息返回token
            String token = SystemCacheUtil.getNewToken(eamUser.getAccount(), eamUser.getName());

            List<String> userTokenList = new ArrayList<>();

            //redis拿出原来tokenList
            EamUser redisUser = SystemCacheUtil.getRedisUser(eamUser.getAccount());
            if (!Objects.isNull(redisUser) && CollectionUtils.isNotEmpty(redisUser.getTokenList())) {
                userTokenList = redisUser.getTokenList();
            }
            eamUser.setTokenList(userTokenList);
            SystemCacheUtil.cacheUserInfo(eamUser, token);

            if (response != null) {
                response.setHeader(Constant.JWT_TOKEN, token);
            }
            log.info("auth 重定向前台页面:{}", workStationPage+token);
            //附加上水司+组织
            StringBuilder sb = new StringBuilder();
            sb.append(token);
            sb.append("&currDs="+appSimpleBeanList.get(0).getAppMark());
            sb.append("&currOrg="+orgVoList.get(0).getId());

            response.sendRedirect(workStationPage + sb.toString());
            return true;
        }
        return false;
    }

    @Override
    public Response<Object> outStafflogin(OutStaffBean outStaffBean) {
        return staffService.outStafflogin(outStaffBean);
    }

    /*
     * 功能描述
     * @author zhouj
     * @date 2021/11/12 10:47
     * @param [ticket, account, request, response]
     * @return com.koron.bean.base.Response
     * desc ticket:集团单点登录使用
     */
    @LoginLog
    @Override
    public Response getSsoLoginUserInfo(String ticket, String account, HttpServletRequest request, HttpServletResponse response) throws IOException {

        // 如果是竹云，使用竹云
        if (Constant.CasType.IAM.equals(casType)) {
            return iamAuthService.getSsoLoginUserInfo(ticket, account, request, response);
        }else if(Constant.CasType.ZSJ.equals(casType)){
            return zsjAuthService.getSsoLoginUserInfo(ticket,account,request,response);
        }

        EamUser eamUser;

        String token = null;

        if (StringUtils.isEmpty(account)) {
            //获取请求里面  原有的token信息. 若原来token是有效,则按照解析出来的账号信息查询最新权限数据返回前端
            token = request.getHeader(Constant.JWT_TOKEN);

            if (StringUtils.isNotEmpty(token)) {
                String preAccount = SystemCacheUtil.verifyToken(token);
                if (StringUtils.isNotEmpty(preAccount)) {
                    account = preAccount;
                } else {
                    //说明token失效,将token重置为null
                    token = null;
                }
            }

            if (!StringUtils.isEmpty(ticket)) {

                Response msg = getAccountByTicket(ticket);

                // 查询条件字符串转对象，查询数据结果
                // ticket有错，可返回异常信息，前端可重定向到系统登录界面，也可在此用response.sendRedirect直接重定向到登录界面
                if (msg.getCode() != 0) {
                    setSSORedirectHeader(response);
                    return Response.fail(msg.getDescription());
                }
                account = (String) msg.getData();
                if (StringUtils.isEmpty(account)) {
                    setSSORedirectHeader(response);
                    return Response.fail("未找到该账号登录oa信息");
                }
            }
        }

        if (StringUtils.isEmpty(account)) {
            log.error("登录账号为空");
            //响应设置 状态码及重定向地址,参数与前端约定. 302 redirecturl
            setSSORedirectHeader(response);
            return Response.fail("登录账号为空");
        }

        // 校验本系统用户表是否有对应账号
        Response res = staffService.queryStaffInfo(account);
        if (!(res.getCode() == 200 && res.getData() != null)) {
            setSSORedirectHeader(response);
            return Response.fail("系统没有该账号信息");
        }

        eamUser = JSON.parseObject(JSON.toJSONString(res.getData()), EamUser.class);

        //返回登录成功信息，前端可做跳转首页处理或者后台直接重定向到系统首页
        //cas认证通过，然后根据信息返回token
        if (StringUtils.isEmpty(token)) {
            token = SystemCacheUtil.getNewToken(eamUser.getAccount(), eamUser.getName());
        }

        List<String> userTokenList = new ArrayList<>();

        //redis拿出原来tokenList
        EamUser redisUser = SystemCacheUtil.getRedisUser(eamUser.getAccount());
        if (!Objects.isNull(redisUser) && CollectionUtils.isNotEmpty(redisUser.getTokenList())) {
            userTokenList = redisUser.getTokenList();
        }
        eamUser.setTokenList(userTokenList);
        SystemCacheUtil.cacheUserInfo(eamUser, token);

        if (response != null) {
            response.setHeader(Constant.JWT_TOKEN, token);
        }
        return Response.ok("登录成功", eamUser);
    }

    @Override
    public Response<EamUser> miniProgramLogin(String code, HttpServletRequest request, HttpServletResponse response) {
        //根据code找到登录用户信息,然后返回token
        String account = null;
        if (StringUtils.isNotEmpty(code)) {
            JSONObject jsonObject = qywxUtil.jscode2session(qywxUtil.getCacheToken(), code);
            account = jsonObject.getString("userid");
        }

        EamUser eamUser;

        String token;

        //获取请求里面  原有的token信息. 若原来token是有效,则按照解析出来的账号信息查询最新权限数据返回前端
        token = request.getHeader(Constant.JWT_TOKEN);

        if (StringUtils.isNotEmpty(token) && StringUtils.isEmpty(code)) {
            String preAccount = SystemCacheUtil.verifyToken(token);
            if (StringUtils.isNotEmpty(preAccount)) {
                account = preAccount;
            } else {
                //说明token失效,将token重置为null
                token = null;
            }
        }

        if (StringUtils.isEmpty(account)) {
            log.error("登录账号为空");
            //响应设置 状态码及重定向地址,参数与前端约定. 302 redirecturl
            setSSORedirectHeader(response);
            return Response.fail("登录账号为空");
        }

        // 校验本系统用户表是否有对应账号
        Response res = staffService.queryStaffInfo(account);
        if (!(res.getCode() == 200 && res.getData() != null)) {
            setSSORedirectHeader(response);
            return Response.fail("系统没有该账号信息");
        }

        eamUser = JSON.parseObject(JSON.toJSONString(res.getData()), EamUser.class);

        //返回登录成功信息，前端可做跳转首页处理或者后台直接重定向到系统首页
        //cas认证通过，然后根据信息返回token
        if (StringUtils.isEmpty(token)) {
            token = SystemCacheUtil.getNewToken(eamUser.getAccount(), eamUser.getName());
        }

        List<String> userTokenList = new ArrayList<>();

        //redis拿出原来tokenList
        EamUser redisUser = SystemCacheUtil.getRedisUser(eamUser.getAccount());
        if (!Objects.isNull(redisUser) && CollectionUtils.isNotEmpty(redisUser.getTokenList())) {
            userTokenList = redisUser.getTokenList();
        }
        eamUser.setTokenList(userTokenList);
        UserOwnJurisdictionBean userOwnJurisdictionBean = eamUser.getUserOwnJurisdictionBean();
        if (!Objects.isNull(userOwnJurisdictionBean)) {
            //移动端用户不返回菜单信息
            userOwnJurisdictionBean.setAppMenuJurisdictionBeanList(null);
        }
        SystemCacheUtil.cacheUserInfo(eamUser, token);

        if (response != null) {
            response.setHeader(Constant.JWT_TOKEN, token);
        }
        return Response.ok("登录成功", eamUser);

    }

    /*
     * 功能描述 oa登录重定向
     * @author zhouj
     * @date 2021/12/31 10:02
     * @param
     * @return
     */
    @Override
    public void oaLoginRedirect(String rsaStr, HttpServletRequest request, HttpServletResponse response) {
        try {
            //解码rsa字符串
            String account = decode(rsaStr);
            //校验account 没有该账号信息应该跳转到前台页面
            // 校验本系统用户表是否有对应账号
            Response res = staffService.queryStaffInfo(account);
            if (!(res.getCode() == 200 && res.getData() != null)) {
                noAccount(response, account);
                return;
            }

            EamUser eamUser = JSON.parseObject(JSON.toJSONString(res.getData()), EamUser.class);

            if (Objects.isNull(eamUser)) {
                noAccount(response, account);
                return;
            }

            if (defaultInfo(eamUser, response)) {
                return;
            }

            log.info("auth 重定向前台页面:{}", oaRedirectPage);
            response.sendRedirect(oaRedirectPage + URLEncoder.encode(rsaStr, "UTF-8"));

        } catch (Exception e) {
            log.error(e.getMessage(), e);

        }

    }

    public void noAccount(HttpServletResponse response, String account) throws IOException {
        response.setContentType("text/html; charset=UTF-8");
        response.getWriter().println("EAM系统没有该账号信息:" + account);
    }

    @Override
    public Response getOaLoginUserInfo(String rsaStr, HttpServletRequest request, HttpServletResponse response) throws IOException {
        String account = null;
        try {
            //解码rsa字符串
            account = decode(rsaStr);
        } catch (Exception e) {
            log.error(e.getMessage(), e);

        }
        return getSsoLoginUserInfo(null, account, request, response);
    }

    /*
     * 功能描述
     * @author zhouj
     * @date 2022/3/17 17:31
     * @param [redirectUrl 用户访问的目标地址, request, response]
     * @return void
     */
    @Override
    public void qywxMessageLogin(String redirectUrl, String code, HttpServletRequest request, HttpServletResponse response) {
        //企业微信点击链接跳转此接口--redirect 用户访问的目标地址 验证了身份信息后,直接重定向到这个地址携带之前的参数
        try {
            String userId = qywxMessageUtil.getUserInfo(code);
            Response resp = getSsoLoginUserInfo(null, userId, request, response);
            if (resp.getCode() == 200) {
                String token = response.getHeader(Constant.JWT_TOKEN);
                response.sendRedirect(redirectUrl + "&token=" + token);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }


    }

    /**
     * 逻辑参考miniProgramLogin()
     * @param code
     * @param request
     * @param response
     * @return
     */
    @Override
    public Response<EamUser> oAuth2Login(String code, HttpServletRequest request, HttpServletResponse response) {
        String account = null;
        if (StringUtils.isNotEmpty(code)) {
            JSONObject jsonObject = qywxUtil.getUserInfo(code);
            if (Objects.isNull(jsonObject) || jsonObject.getString("errcode").equals("40029")){
                return Response.fail("code失效！");
            }
            account = jsonObject.getString("UserId");
        }

        EamUser eamUser;

        String token;

        //获取请求里面  原有的token信息. 若原来token是有效,则按照解析出来的账号信息查询最新权限数据返回前端
        token = request.getHeader(Constant.JWT_TOKEN);

        if (StringUtils.isNotEmpty(token) && StringUtils.isEmpty(code)) {
            String preAccount = SystemCacheUtil.verifyToken(token);
            if (StringUtils.isNotEmpty(preAccount)) {
                account = preAccount;
            } else {
                //说明token失效,将token重置为null
                token = null;
            }
        }

        if (StringUtils.isEmpty(account)) {
            log.error("登录账号为空");
            //响应设置 状态码及重定向地址,参数与前端约定. 302 redirecturl
            setSSORedirectHeader(response);
            return Response.fail("登录账号为空");
        }

        // 校验本系统用户表是否有对应账号
        Response res = staffService.queryStaffInfo(account);
        if (!(res.getCode() == 200 && res.getData() != null)) {
            setSSORedirectHeader(response);
            return Response.fail("系统没有该账号信息");
        }

        eamUser = JSON.parseObject(JSON.toJSONString(res.getData()), EamUser.class);

        //返回登录成功信息，前端可做跳转首页处理或者后台直接重定向到系统首页
        //cas认证通过，然后根据信息返回token
        if (StringUtils.isEmpty(token)) {
            token = SystemCacheUtil.getNewToken(eamUser.getAccount(), eamUser.getName());
        }

        List<String> userTokenList = new ArrayList<>();

        //redis拿出原来tokenList
        EamUser redisUser = SystemCacheUtil.getRedisUser(eamUser.getAccount());
        if (!Objects.isNull(redisUser) && CollectionUtils.isNotEmpty(redisUser.getTokenList())) {
            userTokenList = redisUser.getTokenList();
        }
        eamUser.setTokenList(userTokenList);
        UserOwnJurisdictionBean userOwnJurisdictionBean = eamUser.getUserOwnJurisdictionBean();
        if (!Objects.isNull(userOwnJurisdictionBean)) {
            //移动端用户不返回菜单信息
            userOwnJurisdictionBean.setAppMenuJurisdictionBeanList(null);
        }
        SystemCacheUtil.cacheUserInfo(eamUser, token);

        if (response != null) {
            response.setHeader(Constant.JWT_TOKEN, token);
        }
        return Response.ok("登录成功", eamUser);
    }

    /* uma统一用户*/
    private Response getAccountByTicket(String ticket) {
        String path = casTicketUrl + ticket + "&app=" + Constant.TICKET_APPID;
        log.info("获取用户信息：{}", path);
        //ticket回调获取登录有效用户账户信息
        String jsonStr = HttpUtil.sendPost(path);
        log.info("ticket验证返回数据:{}", jsonStr);
        if (StringUtils.isEmpty(jsonStr)) {
            return Response.fail("ticket获取用户信息失败");
        }
        return JSONObject.parseObject(jsonStr, Response.class);

    }

    private void setSSORedirectHeader(HttpServletResponse response) {
        response.setHeader("redirecturl", redirectUrl);
        response.setStatus(HttpStatus.FOUND.value());
    }

    @Async
    public void saveLoginLog(LoginLogBean loginLogBean) {
        staffService.saveLoginLog(loginLogBean);
    }

    String decode(String rsaStr) throws InvalidKeySpecException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException, InvalidKeyException {
        Key key = RSATool.getKey(new BigInteger(Base64.getDecoder().decode(modulus)), new BigInteger(Base64.getDecoder().decode(publicString)), true);
        byte[] source = RSATool.sign(Base64.getDecoder().decode(rsaStr), key, Cipher.DECRYPT_MODE);
        String account = new String(source);
        log.info("oa跳转rsa解码:" + account);
        String[] split = account.split(":");
        return split[0];
    }

    @Override
    public void decrypt(String encryption, String account, HttpServletResponse response)  throws IOException {
        if(StringUtils.isBlank(encryption)){
            response.setContentType("text/html; charset=UTF-8");
            response.getWriter().println("未找到账号信息1:" + account);
            return;
        }
        if(StringUtils.isBlank(account)){
            response.setContentType("text/html; charset=UTF-8");
            response.getWriter().println("未找到账号信息2:" + account);
            return;
        }

        // 验证账号和加密
        if(!remoteAuthService.verify(encryption,account)){
            response.setContentType("text/html; charset=UTF-8");
            response.getWriter().println("未找到账号信息3:" + account);
            return;
        }

        Response res = staffService.queryStaffInfo(account);
        if (!(res.getCode() == 200 && res.getData() != null)) {
            response.setContentType("text/html; charset=UTF-8");
            response.getWriter().println("系统没有该账号信息4:" + account);
            return;
        }

        EamUser eamUser = JSON.parseObject(JSON.toJSONString(res.getData()), EamUser.class);

        if (Objects.isNull(eamUser)) {
            response.setContentType("text/html; charset=UTF-8");
            response.getWriter().println("系统没有该账号信息5:" + account);
            return;
        }
        if(!remoteAuthService.defaultInfo(eamUser, response)){
            response.setContentType("text/html; charset=UTF-8");
            response.getWriter().println("账号信息有误:" + account);
        }
    }
}
